﻿using FineUICoreDesigner.Base;
using FineUIDesignerVSIX.Handle;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.Editor;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Design;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using Package = Microsoft.VisualStudio.Shell.Package;

namespace FineUICoreDesigner
{
    [PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)]
    [ProvideMenuResource("Menus.ctmenu", 1)]
    [Guid(BaseHelp.PackageGuidString)]
    [ProvideAutoLoad(VSConstants.UICONTEXT.SolutionExistsAndFullyLoaded_string, PackageAutoLoadFlags.BackgroundLoad)]
    public sealed class FineUICoreDesigner : AsyncPackage
    {
        /// <summary>
        /// 事件 Cookie
        /// </summary>
        private uint _rdtEventsCookie;

        private const string webformFilter = ".cshtml";
        private const string RazorPageFilter = webformFilter;

        [Import]
        internal IVsEditorAdaptersFactoryService EditorAdaptersFactoryService { get; set; }

        /// <summary>
        /// Initialization of the package; this method is called right after the package is sited, so this is the place
        /// where you can put all the initialization code that rely on services provided by VisualStudio.
        /// </summary>
        /// <param name="cancellationToken">A cancellation token to monitor for initialization cancellation, which can occur when VS is shutting down.</param>
        /// <param name="progress">A provider for progress updates.</param>
        /// <returns>A task representing the async work of package initialization, or an already completed task if there is none. Do not return null from this method.</returns>
        protected override async Task InitializeAsync(CancellationToken cancellationToken, IProgress<ServiceProgressData> progress)
        {
            // When initialized asynchronously, the current thread may be a background thread at this point.
            // Do any initialization that requires the UI thread after switching to the UI thread.
            await this.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);
            //await base.InitializeAsync(cancellationToken, progress);

            IVsRunningDocumentTable rdt = await GetServiceAsync(typeof(SVsRunningDocumentTable)) as IVsRunningDocumentTable;

            if (rdt != null)
            {
                RunningDocTableEventsHandler rdtEvents = new RunningDocTableEventsHandler(this);

                rdt.AdviseRunningDocTableEvents(rdtEvents, out _rdtEventsCookie);
            }

            //当前解决方案和当前项目
            IVsSolution solution = await GetServiceAsync(typeof(SVsSolution)) as IVsSolution;

            if (solution != null)
            {
                BaseHelp.InitConfig(solution);
            }

            // 注册命令
            //OleMenuCommandService commandService = await GetServiceAsync(typeof(IMenuCommandService)) as OleMenuCommandService;
            //if (commandService != null)
            //{
            //    var menuCommandID = new CommandID(Guid.Parse("your-command-set-guid"), 0x0100);
            //    var menuItem = new MenuCommand(ExecuteGoToDefinition, menuCommandID);
            //    commandService.AddCommand(menuItem);
            //}

            if (await GetServiceAsync(typeof(IMenuCommandService)) is OleMenuCommandService commandService)
            {
                var command = new GoToDefinitionCommand(this);
                command.AddCommand(commandService);
            }
        }

        /// <summary>
        /// 文档监听
        /// </summary>
        private class RunningDocTableEventsHandler : IVsRunningDocTableEvents3
        {
            private readonly FineUICoreDesigner _package;

            /// <summary>
            /// IAfterSave接口方法
            /// </summary>
            private static IEnumerable<Type> afterWeformSaveTypes => Assembly.GetExecutingAssembly()
                .GetTypes().Where(
                t => typeof(IWeformAfterSave).IsAssignableFrom(t) && !t.IsInterface && !t.IsAbstract);

            private static IEnumerable<Type> afterRazorPageSaveTypes => Assembly.GetExecutingAssembly()
                .GetTypes().Where(
                t => typeof(IRazorPageAfterSave).IsAssignableFrom(t) && !t.IsInterface && !t.IsAbstract);

            private static IEnumerable<Type> afterCoreMVCSaveTypes => Assembly.GetExecutingAssembly()
                .GetTypes().Where(
                t => typeof(ICoreMVCAfterSave).IsAssignableFrom(t) && !t.IsInterface && !t.IsAbstract);

            public RunningDocTableEventsHandler(FineUICoreDesigner package)
            {
                _package = package;
            }

            public int OnAfterSave(uint docCookie)
            {
                IVsRunningDocumentTable rdt = (IVsRunningDocumentTable)_package.GetService(typeof(SVsRunningDocumentTable));
                rdt.GetDocumentInfo(docCookie, out uint flags, out uint readLocks, out uint editLocks, out string moniker, out IVsHierarchy hierarchy, out uint itemId, out IntPtr docData);
                if (moniker.EndsWith(webformFilter, StringComparison.OrdinalIgnoreCase)
                    && BaseHelp.Config.isWebForms)
                {
                    BaseHelp.OutputLine($"监听到{webformFilter}文件保存 {moniker}");
                    var info = new WebformFileInfo(moniker);
                    //执行所有 继承IWeformAfterSave 的 Handle方法
                    foreach (var type in afterWeformSaveTypes)
                    {
                        try
                        {
                            IWeformAfterSave instance = (IWeformAfterSave)Activator.CreateInstance(type);
                            instance.Handle(info);
                        }
                        catch { }
                    }
                }
                else if (moniker.EndsWith(RazorPageFilter, StringComparison.OrdinalIgnoreCase)
                    && !BaseHelp.Config.isWebForms && BaseHelp.Config.isRazorPage)
                {
                    BaseHelp.OutputLine($"监听到{webformFilter}文件保存 {moniker}");
                    var info = new RazorPageFileInfo(moniker);
                    foreach (var type in afterRazorPageSaveTypes)
                    {
                        try
                        {
                            IRazorPageAfterSave instance = (IRazorPageAfterSave)Activator.CreateInstance(type);
                            instance.Handle(info);
                        }
                        catch { }
                    }
                }
                else if (moniker.EndsWith(RazorPageFilter, StringComparison.OrdinalIgnoreCase)
                    && !BaseHelp.Config.isRazorPage)
                {
                    var info = new CoreMVCFileInfo(moniker);
                    BaseHelp.OutputLine($"监听到{webformFilter}文件保存 {moniker}");
                    foreach (var type in afterCoreMVCSaveTypes)
                    {
                        try
                        {
                            ICoreMVCAfterSave instance = (ICoreMVCAfterSave)Activator.CreateInstance(type);
                            instance.Handle(info);
                        }
                        catch { }
                    }
                }
                else
                {
                    BaseHelp.OutputLine($"未推断出项目类型，插件默认通过当前文件的后缀，目录结构，appsettings.json配置判断项目类型；");
                }
                return VSConstants.S_OK;
            }

            public int OnAfterFirstDocumentLock(uint docCookie, uint dwRDTLockType, uint dwReadLocksRemaining, uint dwEditLocksRemaining)
            {
                return VSConstants.S_OK;
            }

            public int OnBeforeLastDocumentUnlock(uint docCookie, uint dwRDTLockType, uint dwReadLocksRemaining, uint dwEditLocksRemaining)
            {
                return VSConstants.S_OK;
            }

            public int OnAfterAttributeChange(uint docCookie, uint grfAttribs)
            {
                return VSConstants.S_OK;
            }

            public int OnBeforeDocumentWindowShow(uint docCookie, int fFirstShow, IVsWindowFrame pFrame)
            {
                return VSConstants.S_OK;
            }

            public int OnAfterDocumentWindowHide(uint docCookie, IVsWindowFrame pFrame)
            {
                return VSConstants.S_OK;
            }

            public int OnAfterAttributeChangeEx(uint docCookie, uint grfAttribs, IVsHierarchy pHierOld, uint itemidOld, string pszMkDocumentOld, IVsHierarchy pHierNew, uint itemidNew, string pszMkDocumentNew)
            {
                return VSConstants.S_OK;
            }

            public int OnBeforeSave(uint docCookie)
            {
                ThreadHelper.ThrowIfNotOnUIThread();

                IVsRunningDocumentTable rdt = (IVsRunningDocumentTable)Package.GetGlobalService(typeof(SVsRunningDocumentTable));
                rdt.GetDocumentInfo(docCookie, out uint flags, out uint readLocks, out uint editLocks, out string moniker, out IVsHierarchy hierarchy, out uint itemId, out IntPtr docData);

                if (hierarchy != null)
                {
                    var josn = BaseHelp.GetConfigValue(hierarchy);
                    BaseHelp.Config.isWebForms = BaseHelp.GetEnableWebFormsConfigValue(josn);
                    BaseHelp.Config.FineUIDesigner = BaseHelp.GetFineUIDesignerConfigValue(josn);
                }
                //isRazorPage 页面是否有@page 并且是否有 CsFileName
                if (!string.IsNullOrEmpty(moniker))
                {
                    var FileName = new DirectoryInfo(moniker).Name;
                    var FileRootPath = Path.GetDirectoryName(moniker);
                    var FileContent = File.ReadAllText(moniker);
                    var CsFileName = FileName + ".cs";
                    if (FileContent.Contains("@page")
                        && File.Exists(Path.Combine(FileRootPath, CsFileName)))
                    {
                        BaseHelp.Config.isRazorPage = true;
                    }
                }
                return VSConstants.S_OK;
            }
        }
    }
}
